#define SHA1_C
#include <stdio.h>
#include <stdlib.h>
#include <libmslog/mslog.h>
#include "mscommon.h"
#define FLAG "MSHASH"
#define MSHASH_CLAC_SHIFT(bits, word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32 - (bits))))

typedef struct MSHASHContext{
    ms_u32 msgDigest[5];
    ms_u32 lenLow;
    ms_u32 lenHigh;
    ms_u08 msgBlock[64];
    ms_s32 msgBlock_index;
    ms_s32 computed;
    ms_s32 corrupted;
} MSHASHContext;


static void mshash_inner_processMsgBlock(MSHASHContext *pHashCtt)
{
    const ms_u32 K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6};
    ms_s32 t=0;
    ms_u32 W[80]={0};
    for (t = 0; t < 16; t++){
        W[t] = ((ms_u32)pHashCtt->msgBlock[t * 4]) << 24;
        W[t] |= ((ms_u32)pHashCtt->msgBlock[t * 4 + 1]) << 16;
        W[t] |= ((ms_u32)pHashCtt->msgBlock[t * 4 + 2]) << 8;
        W[t] |= ((ms_u32)pHashCtt->msgBlock[t * 4 + 3]);
    }

    for (t = 16; t < 80; t++){
        W[t] = MSHASH_CLAC_SHIFT(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
    }
	
    ms_u32 A, B, C, D, E;
    A = pHashCtt->msgDigest[0];
    B = pHashCtt->msgDigest[1];
    C = pHashCtt->msgDigest[2];
    D = pHashCtt->msgDigest[3];
    E = pHashCtt->msgDigest[4];

    ms_u32 temp;
    for (t = 0; t < 20; t++){
        temp = MSHASH_CLAC_SHIFT(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = MSHASH_CLAC_SHIFT(30, B);
        B = A;
        A = temp;
    }
    for (t = 20; t < 40; t++){
        temp = MSHASH_CLAC_SHIFT(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = MSHASH_CLAC_SHIFT(30, B);
        B = A;
        A = temp;
    }
    for (t = 40; t < 60; t++){
        temp = MSHASH_CLAC_SHIFT(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = MSHASH_CLAC_SHIFT(30, B);
        B = A;
        A = temp;
    }
    for (t = 60; t < 80; t++){
        temp = MSHASH_CLAC_SHIFT(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = MSHASH_CLAC_SHIFT(30, B);
        B = A;
        A = temp;
    }
    pHashCtt->msgDigest[0] = (pHashCtt->msgDigest[0] + A) & 0xFFFFFFFF;
    pHashCtt->msgDigest[1] = (pHashCtt->msgDigest[1] + B) & 0xFFFFFFFF;
    pHashCtt->msgDigest[2] = (pHashCtt->msgDigest[2] + C) & 0xFFFFFFFF;
    pHashCtt->msgDigest[3] = (pHashCtt->msgDigest[3] + D) & 0xFFFFFFFF;
    pHashCtt->msgDigest[4] = (pHashCtt->msgDigest[4] + E) & 0xFFFFFFFF;
    pHashCtt->msgBlock_index = 0;
}

static void mshash_inner_init(MSHASHContext* pHashCtt)
{
	ms_memset0(pHashCtt, ms_bufsize(MSHASHContext));
	pHashCtt->msgDigest[0] = 0x67452301;
    pHashCtt->msgDigest[1] = 0xEFCDAB89;
    pHashCtt->msgDigest[2] = 0x98BADCFE;
    pHashCtt->msgDigest[3] = 0x10325476;
    pHashCtt->msgDigest[4] = 0xC3D2E1F0;
}

static void mshash_inner_padMsg(MSHASHContext* context)
{
    if (context->msgBlock_index > 55){
        context->msgBlock[context->msgBlock_index++] = 0x80;
        while (context->msgBlock_index < 64){
            context->msgBlock[context->msgBlock_index++] = 0;
        }
        mshash_inner_processMsgBlock(context);
        while (context->msgBlock_index < 56){
            context->msgBlock[context->msgBlock_index++] = 0;
        }
    } else{
        context->msgBlock[context->msgBlock_index++] = 0x80;
        while (context->msgBlock_index < 56){
            context->msgBlock[context->msgBlock_index++] = 0;
        }
    }
    context->msgBlock[56] = (context->lenHigh >> 24) & 0xFF;
    context->msgBlock[57] = (context->lenHigh >> 16) & 0xFF;
    context->msgBlock[58] = (context->lenHigh >> 8) & 0xFF;
    context->msgBlock[59] = (context->lenHigh) & 0xFF;
    context->msgBlock[60] = (context->lenLow >> 24) & 0xFF;
    context->msgBlock[61] = (context->lenLow >> 16) & 0xFF;
    context->msgBlock[62] = (context->lenLow >> 8) & 0xFF;
    context->msgBlock[63] = (context->lenLow) & 0xFF;

    mshash_inner_processMsgBlock(context);
}

static ms_s32 mshash_inner_out(MSHASHContext* context)
{
    if (context->corrupted) {
        return 0;
    }
    if (!context->computed){
        mshash_inner_padMsg(context);
        context->computed = 1;
    }
    return 1;
}

static void mshash_inner_in(MSHASHContext* pHashCtt, const char* msgStr, ms_u32 msgLen)
{
    if (!msgLen){
		ms_errNoret("len = %d", msgLen);
    }

    if (pHashCtt->computed || pHashCtt->corrupted){
        pHashCtt->corrupted = 1;
        return;
    }

    while (msgLen-- && !pHashCtt->corrupted){
        pHashCtt->msgBlock[pHashCtt->msgBlock_index++] = (*msgStr & 0xFF);

        pHashCtt->lenLow += 8;

        pHashCtt->lenLow &= 0xFFFFFFFF;
        if (pHashCtt->lenLow == 0){
            pHashCtt->lenHigh++;
            pHashCtt->lenHigh &= 0xFFFFFFFF;
            if (pHashCtt->lenHigh == 0)
                pHashCtt->corrupted = 1;
        }

        if (pHashCtt->msgBlock_index == 64) {
            mshash_inner_processMsgBlock(pHashCtt);
        }
        msgStr++;
    }
}
/*计算哈希值*/
char* mshash_api_done(const char* source)
{
    MSHASHContext hashCtt;
    mshash_inner_init(&hashCtt);
    mshash_inner_in(&hashCtt, source, ms_buflen(source));
	
    if (!mshash_inner_out(&hashCtt)){
        ms_error("SHA1 ERROR: Could not compute message digest");
		return ms_null;
    }else{
        char* buff = (char*)calloc(128, sizeof(char));
        sprintf(buff, "%08X%08X%08X%08X%08X",
                hashCtt.msgDigest[0],
                hashCtt.msgDigest[1],
                hashCtt.msgDigest[2],
                hashCtt.msgDigest[3],
                hashCtt.msgDigest[4]);
		return buff;
    }
}

#undef SHA1_C

